#ifndef NIGHT_ARRAY_VIEW
#define NIGHT_ARRAY_VIEW

#include "core.hpp"

namespace night {

namespace array {

template< typename T, size_type iSize >
struct array_view<T,iSize> : Expr< array_view<T,iSize>, T &, size<iSize> >
{
	typedef c_array_tag array_category;

	typedef T array_type [iSize];
	typedef typename array_view::result_type result_t;
	typedef T * iterator;
	typedef iterator const_iterator;

	array_type & arr;

	array_view ( array_type & src ) : arr (src) { }

	array_view & operator = ( array_view const & rhs ) { return this->assign (rhs); }
	template< typename RE > array_view & operator = ( RE const & rhs ) { return this->assign (rhs); }

	const_iterator begin () const { return this->arr; }
	const_iterator end () const { return this->arr+iSize; }

	result_t at ( size_type i ) const { return this->arr[i]; }

	template< size_type i >
	pointer_view<T,i> reshape () const { return pointer_view<T,i> ( this->begin () ); }
	template< size_type i, size_type j >
	pointer_view<T,i,j> reshape () const { return pointer_view<T,i,j> ( this->begin () ); }
	template< size_type i, size_type j, size_type k >
	pointer_view<T,i,j,k> reshape () const { return pointer_view<T,i,j,k> ( this->begin () ); }

};


template< typename T, size_type iSize, size_type jSize, size_type kSize >
struct array_view : Expr< array_view<T,iSize,jSize,kSize>, T &, size<iSize,jSize,kSize> >
{
	typedef c_array_tag array_category;

	typedef typename array_view<T,jSize,kSize>::array_type array_type [iSize];
	typedef typename array_view::result_type result_t;

	typedef T * iterator;
	typedef iterator const_iterator;

	array_type & arr;

	array_view ( array_type & src ) : arr (src) { }

	array_view & operator = ( array_view const & rhs ) { return this->assign (rhs); }
	template< typename RE > array_view & operator = ( RE const & rhs ) { return this->assign (rhs); }

	const_iterator begin () const { return array_view<T,jSize,kSize> ( this->arr[0] ).begin (); }
	const_iterator end () const { return this->begin () + this->size (); }

	template< typename I2 >
	result_t at ( size_type i, I2 j ) const { return this->arr[i][j]; }
	template< typename I3 >
	result_t at ( size_type i, size_type j, I3 k ) const { return this->arr[i][j][k]; }

	template< size_type i >
	pointer_view<T,i> reshape () const { return pointer_view<T,i> ( this->begin () ); }
	template< size_type i, size_type j >
	pointer_view<T,i,j> reshape () const { return pointer_view<T,i,j> ( this->begin () ); }
	template< size_type i, size_type j, size_type k >
	pointer_view<T,i,j,k> reshape () const { return pointer_view<T,i,j,k> ( this->begin () ); }

};

template< typename T, size_type iSize, size_type jSize, size_type kSize >
struct pointer_view : Expr< pointer_view<T,iSize,jSize,kSize>, T &, size<iSize,jSize,kSize> >
{
	typedef c_array_tag array_category;

	typedef typename pointer_view::result_type result_t;
	typedef T * iterator;
	typedef iterator const_iterator;

	T * ptr;

	pointer_view ( T * src ) : ptr (src) { }

	pointer_view & operator = ( pointer_view const & rhs ) { return this->assign (rhs); }
	template< typename RE > pointer_view & operator = ( RE const & rhs ) { return this->assign (rhs); }

	const_iterator begin () const { return this->ptr; }
	const_iterator end () const { return this->begin () + this->size (); }

	template<typename I1>
	result_t at ( I1 i ) const { return this->ptr[i]; }
	template<typename I2>
	result_t at ( size_type i, I2 j ) const { return this->ptr[i*jSize+j]; }
	template<typename I3>
	result_t at ( size_type i, size_type j, I3 k ) const { return this->ptr[i*jSize*kSize+j*kSize+k]; }

	template< int i >
	pointer_view<T,i> reshape () const { return pointer_view<T,i> ( this->begin () ); }
	template< int i, int j >
	pointer_view<T,i,j> reshape () const { return pointer_view<T,i,j> ( this->begin () ); }
	template< int i, int j, int k >
	pointer_view<T,i,j,k> reshape () const { return pointer_view<T,i,j,k> ( this->begin () ); }

};

} // end namespace night::array

} // end namespace night

#endif
